/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright(c) 2021 3snic Technologies Co., Ltd */

#ifndef SSSNIC_TX_H
#define SSSNIC_TX_H

#ifdef static
#undef static
#define SSSHW_LLT_STATIC_DEF_SAVED
#endif

#include <linux/types.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>

#ifdef SSSHW_LLT_STATIC_DEF_SAVED
#define static
#undef SSSHW_LLT_STATIC_DEF_SAVED
#endif

#include "sssnic_nicq.h"

#define VXLAN_OFFLOAD_PORT_LE 46354 /* big end is 4789 */

#define COMPACET_WQ_SKB_MAX_LEN 16383

enum tx_offload_type {
	TX_OFFLOAD_TSO = BIT(0),
	TX_OFFLOAD_CSUM = BIT(1),
	TX_OFFLOAD_VLAN = BIT(2),
	TX_OFFLOAD_INVALID = BIT(3),
	TX_OFFLOAD_ESP = BIT(4),
};

struct sssnic_txq_stats {
	u64	packets;
	u64	bytes;
	u64	busy;
	u64	wake;
	u64	dropped;

	/* Subdivision statistics show in private tool */
	u64	skb_pad_err;
	u64	frag_len_overflow;
	u64	offload_cow_skb_err;
	u64	map_frag_err;
	u64	unknown_tunnel_pkt;
	u64	frag_size_err;

#ifdef HAVE_NDO_GET_STATS64
	struct u64_stats_sync	syncp;
#else
	struct u64_stats_sync_empty syncp;
#endif
};

struct sssnic_dma_info {
	dma_addr_t dma;
	u32 len;
};

#define IPV4_VERSION		4
#define IPV6_VERSION		6
#define TCP_HDR_DOFF_UNIT	2
#define TRANSPORT_OFFSET(l4_hdr, skb) ((u32)((l4_hdr) - (skb)->data))

union sssnic_ip {
	struct iphdr *v4;
	struct ipv6hdr *v6;
	unsigned char *hdr;
};

struct sssnic_tx_info {
	struct sk_buff		*skb;

	u16			wqebb_cnt;
	u16			valid_nr_frags;

	int			num_sge;
	u16			num_pkts;
	u64			num_bytes;
	struct sssnic_dma_info	*dma_info;
};

struct sssnic_txq {
	struct net_device	*netdev;
	struct device		*dev;

	struct sssnic_txq_stats txq_stats;

	u8			cos;
	u16			q_id;
	u32			q_mask;
	u32			q_depth;

	struct sssnic_tx_info	*tx_info;
	struct sssnic_io_queue	*sq;

	u64			last_moder_packets;
	u64			last_moder_bytes;
} ____cacheline_aligned;

netdev_tx_t sssnic_lb_xmit_frame(struct sk_buff *skb,
				 struct net_device *netdev);

struct sssnic_dyna_txq_res {
	struct sssnic_tx_info *tx_info;
	struct sssnic_dma_info *bds;
};

/*rx cqe checksum err*/
#define SSSNIC_RX_CSUM_IP_CSUM_ERR	BIT(0)
#define SSSNIC_RX_CSUM_TCP_CSUM_ERR	BIT(1)
#define SSSNIC_RX_CSUM_UDP_CSUM_ERR	BIT(2)
#define SSSNIC_RX_CSUM_IGMP_CSUM_ERR	BIT(3)
#define SSSNIC_RX_CSUM_ICMPV4_CSUM_ERR	BIT(4)
#define SSSNIC_RX_CSUM_ICMPV6_CSUM_ERR	BIT(5)
#define SSSNIC_RX_CSUM_SCTP_CRC_ERR	BIT(6)
#define SSSNIC_RX_CSUM_HW_CHECK_NONE	BIT(7)
#define SSSNIC_RX_CSUM_IPSU_OTHER_ERR	BIT(8)

#define SSSNIC_HEADER_DATA_UNIT 2

struct sssnic_rxq_stats {
	u64	packets;
	u64	bytes;
	u64	errors;
	u64	csum_errors;
	u64	other_errors;
	u64	dropped;
	u64	xdp_dropped;
	u64	rx_buf_empty;

	u64	alloc_skb_err;
	u64	alloc_rx_buf_err;
	u64	xdp_large_pkt;
#ifdef HAVE_NDO_GET_STATS64
	struct u64_stats_sync		syncp;
#else
	struct u64_stats_sync_empty	syncp;
#endif
};

struct sssnic_rx_info {
	dma_addr_t		buf_dma_addr;

	struct sssnic_rq_cqe	*cqe;
	dma_addr_t		cqe_dma;
	struct page		*page;
	u32			page_offset;
	struct sssnic_rq_wqe	*rq_wqe;
	struct sk_buff		*saved_skb;
	u32			skb_len;
};

struct sssnic_rxq {
	struct net_device	*netdev;

	u16			q_id;
	u32			q_depth;
	u32			q_mask;

	u16			buf_len;
	u32			rx_buff_shift;
	u32			dma_rx_buff_size;

	struct sssnic_rxq_stats	rxq_stats;
	u32			cons_idx;
	u32			delta;

	u32			irq_id;
	u16			msix_entry_idx;

	struct sssnic_rx_info	*rx_info;
	struct sssnic_io_queue	*rq;
#ifdef HAVE_XDP_SUPPORT
	struct bpf_prog		*xdp_prog;
#endif

	struct sssnic_irq	*irq_cfg;
	u16			next_to_alloc;
	u16			next_to_update;
	struct device		*dev; /* device for DMA mapping */

	unsigned long		status;
	dma_addr_t		cqe_start_paddr;
	void			*cqe_start_vaddr;

	u64			last_moder_packets;
	u64			last_moder_bytes;
	u8			last_coalesc_timer_cfg;
	u8			last_pending_limt;
} ____cacheline_aligned;

struct sssnic_dyna_rxq_res {
	u16			next_to_alloc;
	struct sssnic_rx_info	*rx_info;
	dma_addr_t		cqe_start_paddr;
	void			*cqe_start_vaddr;
};

netdev_tx_t sssnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev);

void sssnic_txq_get_stats(struct sssnic_txq *txq,
			  struct sssnic_txq_stats *stats);

void sssnic_txq_clean_stats(struct sssnic_txq_stats *txq_stats);

struct sssnic_nic_dev;
int sssnic_alloc_txqs_res(struct sssnic_nic_dev *nic_dev, u16 num_sq,
			  u32 sq_depth, struct sssnic_dyna_txq_res *txqs_res);

void sssnic_free_txqs_res(struct sssnic_nic_dev *nic_dev, u16 num_sq,
			  u32 sq_depth, struct sssnic_dyna_txq_res *txqs_res);

int sssnic_configure_txqs(struct sssnic_nic_dev *nic_dev, u16 num_sq,
			  u32 sq_depth, struct sssnic_dyna_txq_res *txqs_res);

int sssnic_alloc_txqs(struct net_device *netdev);

void sssnic_free_txqs(struct net_device *netdev);

int sssnic_tx_poll(struct sssnic_txq *txq, int budget);

int sssnic_flush_txqs(struct net_device *netdev);

void sssnic_set_txq_cos(struct sssnic_nic_dev *nic_dev, u16 start_qid,
			u16 q_num, u8 cos);

#ifdef static
#undef static
#define SSSHW_LLT_STATIC_DEF_SAVED
#endif

static inline __sum16 csum_magic(union sssnic_ip *ip, unsigned short proto)
{
	return (ip->v4->version == IPV4_VERSION) ?
		csum_tcpudp_magic(ip->v4->saddr, ip->v4->daddr, 0, proto, 0) :
		csum_ipv6_magic(&ip->v6->saddr, &ip->v6->daddr, 0, proto, 0);
}

#ifdef SSSHW_LLT_STATIC_DEF_SAVED
#define static
#undef SSSHW_LLT_STATIC_DEF_SAVED
#endif



int sssnic_alloc_rxqs(struct net_device *netdev);

void sssnic_free_rxqs(struct net_device *netdev);

int sssnic_alloc_rxqs_res(struct sssnic_nic_dev *nic_dev, u16 num_rq,
			  u32 rq_depth, struct sssnic_dyna_rxq_res *rxqs_res);

void sssnic_free_rxqs_res(struct sssnic_nic_dev *nic_dev, u16 num_rq,
			  u32 rq_depth, struct sssnic_dyna_rxq_res *rxqs_res);

int sssnic_configure_rxqs(struct sssnic_nic_dev *nic_dev, u16 num_rq,
			  u32 rq_depth, struct sssnic_dyna_rxq_res *rxqs_res);

int sssnic_rx_configure(struct net_device *netdev, u8 dcb_en);

void sssnic_rx_remove_configure(struct net_device *netdev);

int sssnic_rx_poll(struct sssnic_rxq *rxq, int budget);

void sssnic_rxq_get_stats(struct sssnic_rxq *rxq,
			  struct sssnic_rxq_stats *stats);

void sssnic_rxq_clean_stats(struct sssnic_rxq_stats *rxq_stats);

#endif
